#pragma once
#include<grpcpp/grpcpp.h>
#include"message.grpc.pb.h"
#include"const.h"
#include"Singleton.h"

using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;

using message::GetVarifyReq;
using message::GetVarifyRsp;
using message::VarifyService;

class RPCConPool {
public:
    RPCConPool(size_t poolsize, std::string host, std::string port) :
        poolSize_(poolsize), host_(host), port_(port), b_stop_(false) {
        for (size_t i = 0; i < poolSize_; i++) {
            std::shared_ptr<Channel> channel = grpc::CreateChannel(host + ":" + port,
                grpc::InsecureChannelCredentials());
            connections_.push(VarifyService::NewStub(channel));
        }
    }

    ~RPCConPool() {
        std::lock_guard<std::mutex> lock(mutex_);
        while (!connections_.empty()) {
            connections_.pop();
        }  
    }

    void Close() {
        b_stop_ = true;
        cond_.notify_all();
    }

    std::unique_ptr<VarifyService::Stub > getConnection() {//��ȡ��ʹ
        std::unique_lock<std::mutex> lock(mutex_);
        cond_.wait(lock, [this]() {
            if (b_stop_) {
                return true;
            }
            return !connections_.empty();
            });
        if (b_stop_) {
            return nullptr;
        }
        auto context = std::move(connections_.front());
        connections_.pop();
        return context;
    }

    void releaseConnection(std::unique_ptr<VarifyService::Stub> context) {//�ͷ���ʹ
        std::lock_guard<std::mutex> lock(mutex_);
        if (b_stop_) {
            return;
        }
        connections_.push(std::move(context));
        cond_.notify_one();
    }
private:
    std::atomic<bool> b_stop_;//�ǲ��ǻ���
    size_t poolSize_;
    std::string host_;
    std::string port_;
    std::queue<std::unique_ptr<VarifyService::Stub>> connections_;//��ʹ����
    std::mutex mutex_;//������
    std::condition_variable cond_;//��������
};

class VerifyGrpcClient:public Singleton<VerifyGrpcClient>
{
	friend class Singleton<VerifyGrpcClient>;
public:
	GetVarifyRsp GetVarifyCode(std::string email) {
		ClientContext context;
		GetVarifyReq request;
		GetVarifyRsp reply;
		request.set_email(email);
        auto stub = pool_->getConnection();
		Status status = stub->GetVarifyCode(&context, request, &reply);
		if (status.ok()) {
            pool_->releaseConnection(std::move(stub));//����
			return reply;
		}
		else {
            pool_->releaseConnection(std::move(stub));
			reply.set_error(ErrorCodes::RPCFailed);
			return reply;
		}

	}
private:
    VerifyGrpcClient();
    std::unique_ptr<RPCConPool> pool_;
};

